﻿using System;
using System.Collections.Generic;
using Newtonsoft.Json.Linq;
using xbmc_json_async.Media;
using xbmc_json_async.System;

using xbmc_json_async.Types;

namespace xbmc_json_async.Library
{
    public class XAudioLibrary
    {
        /*
         *  Specification: http://wiki.xbmc.org/index.php?title=JSON-RPC_API/v4#AudioLibrary
         *  API Methods:
         *      AudioLibrary.Clean (implemented)
         *      AudioLibrary.Export (not implemented)
         *      AudioLibrary.GetAlbumDetails (implemented)
         *      AudioLibrary.GetAlbums (implemented)
         *      AudioLibrary.GetArtistDetails (implemented)
         *      AudioLibrary.GetArtists (implemented)
         *      AudioLibrary.GetGenres (implemented)
         *      AudioLibrary.GetRecentlyAddedAlbums
         *      AudioLibrary.GetRecentlyAddedSongs (implemented)
         *      AudioLibrary.GetSongDetails (implemented)
         *      AudioLibrary.GetSongs (implemented)
         *      AudioLibrary.Scan (implemented)         
         * */
        private readonly XClient mClient;
        private XError mError;

        //either fields or properties
        private string _propType = "properties";

        /// <summary>
        /// Initializes a new instance of the <see cref="XAudioLibrary"/> class.
        /// </summary>
        /// <param name="client">The client.</param>
        public XAudioLibrary(XClient client)
        {
            mClient = client;
        }

        /// <summary>
        /// Cleans the audio library of non existent items.
        /// </summary>
        /// <param name="userCallback">The user callback.</param>
        public void Clean(XDataReceived userCallback)
        {
            mClient.GetData("AudioLibrary.Clean", null, CleanCallback, userCallback);
        }

        /// <summary>
        /// Cleans the callback.
        /// </summary>
        /// <param name="requestState">State of the request.</param>
        private void CleanCallback(XRequestState requestState)
        {
            var query = JObject.Parse(requestState.ResponseData);
            var result = (JValue)query["result"];

            if (requestState.UserCallback != null)
            {
                requestState.UserCallback(result.Value.ToString());
            }
        }

        /// <summary>
        /// Exports the specified export path.
        /// </summary>
        /// <param name="exportPath">The export path.</param>
        /// <param name="includeImages">if set to <c>true</c> [include images].</param>
        /// <param name="overwrite">if set to <c>true</c> [overwrite].</param>
        /// <param name="userCallback">The user callback.</param>
        public void Export(string exportPath, bool includeImages, bool overwrite, XDataReceived userCallback)
        {
            //throw new NotImplementedException("Not supported due to JSON complexity");
            var args = new JObject(
                new JProperty("type",
                    new JArray(
                        new JObject(new JProperty("path", exportPath)),
                        new JObject(
                            new JProperty("images", includeImages),
                            new JProperty("overwrite", overwrite)

                            )
                        )
                    )
                );


           

            //var args = new JObject(
                //new JProperty("type",
                //    new JObject(
                //        new JObject("properties",
                //            new JProperty("path",exportPath)

                //            )

                //        ),
                //    new JObject(
                //         new JObject("properties",
                //             new JProperty("images", includeImages),
                //             new JProperty("overwrite", overwrite)
                            
                //        )


                //    )
                //));
            /*var args = new JObject(new JProperty("properties",
                    new JArray(
                    new JObject(new JProperty("path", exportPath)),
                    new JObject(new JProperty("images", includeImages)),
                    new JObject(new JProperty("overwrite", overwrite)))));*/

            mClient.GetData("AudioLibrary.Export", args, ExportCallback, userCallback);
        }
        
        /// <summary>
        /// Exports the callback.
        /// </summary>
        /// <param name="requestState">State of the request.</param>
        private void ExportCallback(XRequestState requestState)
        {
            var query = JObject.Parse(requestState.ResponseData);

            if (XHelpers.CheckResponseForError(query, out mError))
            {

            }
            if (requestState.UserCallback != null)
            {
                requestState.UserCallback(query);
                //var result = (JObject)query["result"];
            }
        }

        /// <summary>
        /// Gets the album details.
        /// Returns Album to usercallback
        /// </summary>
        /// <param name="albumId">The album id.</param>
        /// <param name="userCallback">The user callback.</param>
        public void GetAlbumDetails(int albumId, XDataReceived userCallback)
        {
            var args = new JObject { new JProperty("albumid", albumId), new JProperty(_propType, Types.Audio.Fields.Album.Get()) };
            mClient.GetData("AudioLibrary.GetAlbumDetails", args, GetAlbumDetailsCallback, userCallback);
        }
        
        /// <summary>
        /// Gets the album details callback.
        /// </summary>
        /// <param name="requestState">State of the request.</param>
        private void GetAlbumDetailsCallback(XRequestState requestState)
        {
            JObject query = JObject.Parse(requestState.ResponseData);
            var result = (JObject)query["result"];

            Album album = null;

            if (result["albumdetails"] != null)
            {
                JObject item = (JObject)result["albumdetails"];
                album = Album.FromJsonObject(item);

            }
            if (requestState.UserCallback != null)
            {
                requestState.UserCallback(album);
            }
        }

        /// <summary>
        /// Gets the albums.
        /// </summary>
        /// <param name="sort">The sort.</param>
        /// <param name="userCallback">The user callback.</param>
        public void GetAlbums(SortParams sort, XDataReceived userCallback)
        {
            var args = new JObject { new JProperty(_propType, Types.Audio.Fields.Album.Get()) };
            
            if (sort != null)
                args.Add(sort.ToJObject().Children());

            mClient.GetData("AudioLibrary.GetAlbums", args, GetAlbumsCallback, userCallback);
        }

        /// <summary>
        /// Gets the albums by artist.
        /// </summary>
        /// <param name="artistId">The artist id.</param>
        /// <param name="sort">The sort.</param>
        /// <param name="userCallback">The user callback.</param>
        public void GetAlbumsByArtist(int artistId, SortParams sort, XDataReceived userCallback)
        {
            var args = new JObject { new JProperty("artistid", artistId), new JProperty(_propType, Types.Audio.Fields.Album.Get()) };

            if (sort != null)
                args.Add(sort.ToJObject().Children());

            mClient.GetData("AudioLibrary.GetAlbums", args, GetAlbumsCallback, userCallback);
        }

        /// <summary>
        /// Gets the albums callback.
        /// </summary>
        /// <param name="requestState">State of the request.</param>
        private void GetAlbumsCallback(XRequestState requestState)
        {
            var albums = new Albums();//new List<Album>();

            JObject query = JObject.Parse(requestState.ResponseData);
            var result = (JObject)query["result"];

            albums.LoadFromJsonObject(result);
            
            if (requestState.UserCallback != null)
                requestState.UserCallback(albums);
        }

        /// <summary>
        /// Gets the artist details.
        /// returns an Artist object to the user callback
        /// </summary>
        /// <param name="artistId">The artist id.</param>
        /// <param name="userCallback">The user callback.</param>
        public void GetArtistDetails(int artistId, XDataReceived userCallback)
        {
            var args = new JObject { new JProperty("artistid", artistId), new JProperty(_propType, Types.Audio.Fields.Artist.Get()) };
            //throw new NotImplementedException("API returns invalid parameters for this method");
            //var args = new JObject { new JProperty("artistid", artistId), new JProperty(_propType, Artist.PropertyFields) };
            mClient.GetData("AudioLibrary.GetArtistDetails", args, GetArtistDetailsCallback, userCallback);
        }
        
        /// <summary>
        /// Gets the artist details callback.
        /// </summary>
        /// <param name="requestState">State of the request.</param>
        private void GetArtistDetailsCallback(XRequestState requestState)
        {
            

            JObject query = JObject.Parse(requestState.ResponseData);

            if (XHelpers.CheckResponseForError(query, out mError))
            {
                //error handling needed here
            }
            else
            {

                var result = (JObject)query["result"];

                Artist artist = null;

                if (result["artistdetails"] != null)
                {
                    JObject item = (JObject)result["artistdetails"];
                    artist = Artist.FromJsonObject(item, false);

                }
                if (requestState.UserCallback != null)
                {
                    requestState.UserCallback(artist);
                }
            }
        }
        
        /// <summary>
        /// Gets the artists.
        /// </summary>
        /// <param name="sort">The sort.</param>
        /// <param name="userCallback">The user callback.</param>
        public void GetArtists(SortParams sort, XDataReceived userCallback)
        {
            var args = new JObject();

            args.Add(new JProperty(_propType, Types.Audio.Fields.Artist.Get()));
 
            if (sort != null)
                args.Add(sort.ToJObject().Children());

            mClient.GetData("AudioLibrary.GetArtists", args, GetArtistsCallback, userCallback);
        }

        /// <summary>
        /// Gets the artists callback.
        /// </summary>
        /// <param name="requestState">State of the request.</param>
        private void GetArtistsCallback(XRequestState requestState)
        {
            var artists = new Artists();

            var query = JObject.Parse(requestState.ResponseData);

            if(XHelpers.CheckResponseForError(query,out mError))
            {

            }
            else
            {
        

                var result = (JObject) query["result"];

                artists.LoadFromJsonObject(result, false);

                if (requestState.UserCallback != null)
                    requestState.UserCallback(artists);
            }
        }

        /// <summary>
        /// Gets the genres.
        /// </summary>
        /// <param name="userCallback">The user callback.</param>
        public void GetGenres(XDataReceived userCallback)
        {
            var args = new JObject();

            args.Add(new JProperty(_propType, xbmc_json_async.Types.Library.Fields.Genre.Get()));


            mClient.GetData("AudioLibrary.GetGenres", args, GetGenresCallback, userCallback);
        }
        
        /// <summary>
        /// Gets the genres callback.
        /// </summary>
        /// <param name="requestState">State of the request.</param>
        private void GetGenresCallback(XRequestState requestState)
        {
            JObject query = JObject.Parse(requestState.ResponseData);

            var genres = new Genres();

            if (XHelpers.CheckResponseForError(query, out mError))
            {
                //error handling needed here
            }
            else
            {

                var result = (JObject)query["result"];

                genres.LoadFromJsonObject(result);

                if (requestState.UserCallback != null)
                {
                    requestState.UserCallback(genres);
                }
            }
        }

        /// <summary>
        /// Gets the recently added albums.
        /// </summary>
        /// <param name="sort">The sort.</param>
        /// <param name="userCallback">The user callback.</param>
        public void GetRecentlyAddedAlbums(SortParams sort, XDataReceived userCallback)
        {
            var args = new JObject { new JProperty(_propType, Types.Audio.Fields.Album.Get()) };

            if (sort != null)
                args.Add(sort.ToJObject().Children());

            mClient.GetData("AudioLibrary.GetRecentlyAddedAlbums", args, GetAlbumsCallback, userCallback);
        }


        /// <summary>
        /// Gets the recently added songs.
        /// </summary>
        /// <param name="sort">The sort.</param>
        /// <param name="userCallback">The user callback.</param>
        public void GetRecentlyAddedSongs(SortParams sort, XDataReceived userCallback)
        {
            var args = new JObject { new JProperty(_propType, Types.Audio.Fields.Song.Get()) };

            if (sort != null)
                args.Add(sort.ToJObject().Children());

            mClient.GetData("AudioLibrary.GetRecentlyAddedSongs", args, GetSongsCallback, userCallback);
        }

        /// <summary>
        /// Gets the songs.
        /// </summary>
        /// <param name="sort">The sort.</param>
        /// <param name="userCallback">The user callback.</param>
        public void GetSongs(SortParams sort, XDataReceived userCallback)
        {
            var args = new JObject { new JProperty(_propType, Types.Audio.Fields.Song.Get()) };

            if (sort != null)
                args.Add(sort.ToJObject().Children());

            mClient.GetData("AudioLibrary.GetSongs", args, GetSongsCallback, userCallback);
        }

        /// <summary>
        /// Gets the songs by artist.
        /// </summary>
        /// <param name="artistId">The artist id.</param>
        /// <param name="sort">The sort.</param>
        /// <param name="userCallback">The user callback.</param>
        public void GetSongsByArtist(int artistId, SortParams sort, XDataReceived userCallback)
        {
            var args = new JObject { new JProperty("artistid", artistId), new JProperty(_propType, Types.Audio.Fields.Song.Get()) };

            if (sort != null)
                args.Add(sort.ToJObject().Children());

            mClient.GetData("AudioLibrary.GetSongs", args, GetSongsCallback, userCallback);
        }

        /// <summary>
        /// Gets the songs by album.
        /// </summary>
        /// <param name="albumId">The album id.</param>
        /// <param name="sort">The sort.</param>
        /// <param name="userCallback">The user callback.</param>
        /// <param name="actionCallback">The action callback.</param>
        public void GetSongsByAlbum(int albumId, SortParams sort, XDataReceived userCallback, Action<List<Song>> actionCallback)
        {
            var args = new JObject { new JProperty("albumid", albumId), new JProperty(_propType, Types.Audio.Fields.Song.Get()) };

            if (sort != null)
                args.Add(sort.ToJObject().Children());

            mClient.GetData("AudioLibrary.GetSongs", args, GetSongsCallback, userCallback);
        }

        /// <summary>
        /// Gets the songs by genre.
        /// </summary>
        /// <param name="genreId">The genre id.</param>
        /// <param name="sort">The sort.</param>
        /// <param name="userCallback">The user callback.</param>
        public void GetSongsByGenre(int genreId, SortParams sort, XDataReceived userCallback)
        {
            var args = new JObject { new JProperty("genreid",genreId), new JProperty(_propType, Types.Audio.Fields.Song.Get()) };

            if (sort != null)
                args.Add(sort.ToJObject().Children());

            mClient.GetData("AudioLibrary.GetSongs", args, GetSongsCallback, userCallback);
        }
        
        /// <summary>
        /// Gets the songs callback.
        /// </summary>
        /// <param name="requestState">State of the request.</param>
        private void GetSongsCallback(XRequestState requestState)
        {
            var songs = new Songs();

            var query = JObject.Parse(requestState.ResponseData);

            if (XHelpers.CheckResponseForError(query, out mError))
            {
                //error handling needed here
            }
            else
            {
                var result = (JObject)query["result"];

                songs.LoadFromJsonObject(result);

                if (requestState.UserCallback != null)
                    requestState.UserCallback(songs);
            }
        }

        /// <summary>
        /// Scans the specified user callback.
        /// </summary>
        /// <param name="userCallback">The user callback.</param>
        public void Scan(XDataReceived userCallback)
        {
            mClient.GetData("AudioLibrary.Scan", null, ScanCallback, userCallback);
        }
        
        /// <summary>
        /// Scans the callback.
        /// </summary>
        /// <param name="requestState">State of the request.</param>
        private void ScanCallback(XRequestState requestState)
        {
            var query = JObject.Parse(requestState.ResponseData);
            var result = (JValue)query["result"];

            if (requestState.UserCallback != null)
            {
                requestState.UserCallback(result.Value.ToString());
            }
        }
    }
}